package wrice.esm.compiler;

import java.util.HashMap;
import java.util.Scanner;
import java.util.StringTokenizer;

import wrice.esm.compiler.fsm.ParserFSM;
import wrice.esm.compiler.fsm.ParserFSM.*;
import wrice.esm.compiler.fsm.event.*;

public class Parser {
	Parser( Scanner InInput )
	{
		input = InInput;
		parse();
	}

	HashMap<String, String> globalVariables_ = new HashMap<String, String>();
	HashMap<String, Events> states_ = new HashMap<String,Events>();

	private transient Scanner input;
	private transient String tmpVariableName_;
	private transient Events events_ = null;

	static class Events
	{
		static class Event
		{
			String nextState_ = null;
			String action_ = null;
		}
		
		public HashMap< String, Event > events_ = new HashMap<String, Event>();
	}
	
	private void parse()
	{
		GlobalDefinitionState globalState = null;
		GlobalDefinition_ActorState globalActorState = null;
		GlobalDefinition_InitialStateState globalInitialState = null;
		GlobalVariableValueState globalVariableValueState = null;
		StateDefinitionSetState definitionSetState = null;
		StateDefinitionState definitionState = null;
		EventSetState eventSetState = null;
		EventValueState eventValueState = null;
		EndState endState = null;

		globalState = ParserFSM.CreateFSM( this );

		while(input.hasNext()) {
		    String nextToken = input.next();

		    // global state
		    if( globalState.isCurrentState() )
		    {
			    if( nextToken.equalsIgnoreCase( "Actor" ) )
			    	globalActorState = globalState.event( new ActorEvent() );
			    else if( nextToken.equalsIgnoreCase( "InitialState" ) )
			    	globalInitialState = globalState.event( new InitialStateEvent() );
			    else if( nextToken.equalsIgnoreCase( "{" ) )
			    	definitionSetState = globalState.event( new BlockBeginEvent() );
			    else if( nextToken.startsWith( "//" ) )
			    	globalState = globalState.event( new CommentLineEvent() );
			    else if( nextToken.startsWith( "/*" ) )
			    	globalState = globalState.event( new CommentBlockEvent() );
			    else globalVariableValueState = globalState.event( new VariableNameEvent( nextToken ) );
		    }
		    else if( null != globalInitialState && globalInitialState.isCurrentState() )
		    {
		    	globalState = globalInitialState.event( new StringValueEvent( nextToken ) );
		    }
		    else if( null != globalVariableValueState && globalVariableValueState.isCurrentState() )
		    {
		    	globalState = globalVariableValueState.event( new StringValueEvent( nextToken ) );
		    }
		    //else if( null == globalActorState )	// required
		    //	throw new InvalidStateDefinitionRuntimeException();
		    else if( null != globalActorState && globalActorState.isCurrentState() )
		    {
		    	globalState = globalActorState.event( new StringValueEvent( nextToken ) );
		    }
		    // definition Set state
		    else if( null != definitionSetState && definitionSetState.isCurrentState() )
		    {
		    	if( nextToken.equalsIgnoreCase( "}" ) )
		    		endState = definitionSetState.event( new BlockEndEvent() );
			    else if( nextToken.startsWith( "//" ) )
			    	definitionSetState = definitionSetState.event( new CommentLineEvent() );
			    else if( nextToken.startsWith( "/*" ) )
			    	definitionSetState = definitionSetState.event( new CommentBlockEvent() );
		    	else definitionState = definitionSetState.event( new StateNameEvent( nextToken ) );
		    }
		    // definition state
		    else if( null != definitionState && definitionState.isCurrentState() )
		    {
		    	if( nextToken.equalsIgnoreCase( "{" ) )
		    		eventSetState = definitionState.event( new BlockBeginEvent() );
		    	else if( nextToken.equalsIgnoreCase( "{}" ) )
		    		definitionSetState = definitionState.event( new BlockEmptyEvent() );
			    else if( nextToken.startsWith( "//" ) )
			    	definitionState = definitionState.event( new CommentLineEvent() );
			    else if( nextToken.startsWith( "/*" ) )
			    	definitionState = definitionState.event( new CommentBlockEvent() );
		    	else throw new InvalidStateMachineDefinitionRuntimeException();
		    }
		    // event set state
		    else if( null != eventSetState && eventSetState.isCurrentState() )
		    {
		    	if( nextToken.equalsIgnoreCase( "}" ) )
		    		definitionSetState = eventSetState.event( new BlockEndEvent() );
			    else if( nextToken.startsWith( "//" ) )
			    	eventSetState = eventSetState.event( new CommentLineEvent() );
			    else if( nextToken.startsWith( "/*" ) )
			    	eventSetState = eventSetState.event( new CommentBlockEvent() );
		    	else eventValueState = eventSetState.event( new EventNameEvent( nextToken ) );
		    }
		    // EventValue state
		    else if( null != eventValueState && eventValueState.isCurrentState() )
		    {
			    if( nextToken.startsWith( "//" ) )
			    	eventValueState = eventValueState.event( new CommentLineEvent() );
			    else if( nextToken.startsWith( "/*" ) )
			    	eventValueState = eventValueState.event( new CommentBlockEvent() );
			    else eventSetState = eventValueState.event( new StateNameEvent( nextToken ) );
		    }
		    // end state
		    else if( null == endState )
		    	throw new InvalidStateMachineDefinitionRuntimeException();
		}
	}

	public void skipCommentBlock( CommentBlockEvent evt )
	{
		while(input.hasNext()) {
		    String nextToken = input.next();
		    if( nextToken.endsWith( "*/" )) break;
		}
	}
	
	public void skipCommentLine( CommentLineEvent evt )
	{
		if( input.hasNext() )
			input.nextLine();
	}

	public void readGlobalActor( StringValueEvent evt )
	{
		globalVariables_.put( "actor", evt.stringValue_ );
		input.nextLine();
	}
	
	public void readGlobalInitialState( StringValueEvent evt )
	{
		globalVariables_.put( "initialstate", evt.stringValue_ );
		input.nextLine();
	}
	
	public void readGlobalVariableName( VariableNameEvent evt )
	{
		tmpVariableName_ = evt.variableName_.toLowerCase();
	}

	public void readGlobalVariableValue( StringValueEvent evt )
	{
		globalVariables_.put( tmpVariableName_, evt.stringValue_);
		input.nextLine();
	}
	
	public void readStateName( StateNameEvent evt )
	{
		events_ =new Events(); 
		states_.put( evt.stateName_, events_ );
		
		if( !globalVariables_.containsKey("initialstate") )
			globalVariables_.put( "initialstate", evt.stateName_ );
	}
	
	public void beginEventSet( BlockBeginEvent evt )
	{}

	public void emptyEventSet( BlockEmptyEvent evt )
	{
		endEventSet( new BlockEndEvent() );
	}
	
	public void endEventSet( BlockEndEvent evt )
	{}

	public 	void readEventName( EventNameEvent evt )
	{
		tmpVariableName_ =evt.eventName_;
		events_.events_.put(evt.eventName_, new Events.Event() );
	}
	
	public void readEventNextStateAndAction( StateNameEvent evt )
	{
		Events.Event event = events_.events_.get( tmpVariableName_ );
		event.nextState_ = evt.stateName_;
		
		String nextLine = input.nextLine();
		StringTokenizer st = new StringTokenizer( nextLine );
		if( !st.hasMoreTokens() ) return;

		String nextToken = st.nextToken();
		if( nextToken.startsWith( "//" ) ) return;
		if( nextToken.startsWith( "/*" ) ) return;
		
		event.action_ = nextToken;
	}
}
